#!/usr/bin/env python
# -*- coding: utf-8 -*-
# @Time    : 2021/5/8 12:44
# @Author  : LiShan
# @Email   : lishan_1997@126.com
# @File    : timming_fix.py
# @Note    : this is note
import os
import string
import time

import matplotlib.pyplot as plt
import numpy as np
import pandas as pd

from environment.vissim import VisCom

project_path = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
net_path = project_path.replace("\\", "/") + "/resource/vissim/net/net.inp"
simulation = [999999, 0, 1, 1, 42, False, False]
test_step = 42
plan_file = "./fix/txt/test_fix_plans_define.txt"
result_file = "./fix/txt/test_fix_record.txt"
plans_file = "./fix/txt/test_fix_plans.txt"
save_img1 = "./fix/png/bar_fix_delay.png"
save_img2 = "./fix/png/min_fix_timing.png"


# 生成配时方案
def create_plans(para, file, plan_num=-1):
    # 配时范围
    cycle_time = para[0]
    green_low = para[1]
    green_high = para[2]
    green_interval = para[3]
    amber_time = para[4]
    clearing_time = para[5]
    phase_num = len(clearing_time)
    loss_time = amber_time * phase_num + sum(clearing_time)
    try:
        with open(file, "a+") as f:
            f.truncate(0)
    except (IndexError, Exception):
        pass
    from itertools import permutations
    a = list(permutations(range(green_low, green_high + 1, green_interval), phase_num))
    plans = []
    unused_plan = 0
    for i in range(len(a)):
        if sum(a[i]) == cycle_time - loss_time:
            if plan_num < 0 or len(plans) < plan_num:
                plan = [cycle_time, amber_time, clearing_time, list(a[i])]
                plans.append(plan)
                with open(file, "a+") as f:
                    line = "%s\n" % (str(plan))
                    f.write(line)
            else:
                unused_plan += 1
    return plans, unused_plan


# 读取配时方案
def read_plans(file):
    plans = []
    with open(file, "r") as f:
        data = f.read()
        data = data.split("\n")
        for i in range(len(data)):
            if data[i] != "":
                data[i] = eval(data[i])
                plans.append(data[i])
    return plans


# 测试固定配时方案
def test_fix_timming(env, plans):
    print("共%d种方案" % len(plans))
    print("开始测试......")
    try:
        with open(result_file, "a+") as f:
            f.truncate(0)
    except (IndexError, Exception):
        pass
    for plan in range(len(plans)):
        # 启动时间
        test_start = time.perf_counter()
        # 初始化参数
        test_delay_record = []
        # 可视化visism仿真图像
        # env.render()
        # 设置随机数种子
        # env.seed(42)
        # 重置环境获取初始交通流状态
        env.reset()
        # 热身时间
        for i in range(5):
            env.step(0)
        try:
            with open(result_file[:-4] + '_' + str(plan + 1) + '.txt', "a+") as f:
                f.truncate(0)
        except (IndexError, Exception):
            pass
        # 仿真运行指定个周期
        for step in range(test_step):
            progress = int((step + 1) / test_step * 100)
            print("\r正在测试固定配时方案" + str(plan + 1) + ":" + "|" + "#" * progress + " " * (100 - progress) + "|"
                  + str(progress) + "%", end="")
            # vissim环境采取固定动作运行一周期，得到下一周期的状态信息
            observation, reward, done, info = env.step(plan)
            # 延误
            delay = reward
            test_delay_record.append(delay)
            # 保存测试记录信息
            with open(result_file[:-4] + '_' + str(plan + 1) + '.txt', 'a+') as f:
                record = "%s\t%s\t\n" % (str(step + 1), str(delay))
                f.write(record)
        # 输出配时方案的测试延误值
        test_mean_delay = sum(test_delay_record) / len(test_delay_record)
        info = '\ntest plan: {}, test_mean_delay: {}'.format(plan + 1, round(test_mean_delay, 3))
        print(info)
        # 保存配时方案测试记录信息
        with open(result_file, 'a+') as f:
            record = "%s\t%s\t%s\t\n" % (str(plan + 1), str(test_step), str(round(test_mean_delay, 3)))
            f.write(record)
        # 输出测试时间
        test_time = time.perf_counter() - test_start
        h, ss = divmod(test_time, 3600)
        m, s = divmod(ss, 60)
        info = "complete test time: {} second, that is {} hour, {} minute, {} second".format(test_time, h, m, s)
        print(info)
    env.close()


# 绘制测试结果
def draw_result():
    font = {'family': 'SimSun',
            'weight': 'bold',
            'size': '16'}
    plt.rc('font', **font)
    plt.rc('axes', unicode_minus=False)
    plt.rcParams['figure.facecolor'] = "#FFFFF0"  # 设置窗体颜色
    plt.rcParams['axes.facecolor'] = "#FFFFF0"  # 设置绘图区颜色
    names = ["plan", "step", "delay"]
    data = pd.read_csv(result_file, sep="\s+", names=names)
    delay = list(data["delay"].values)
    """延误"""
    min_delay = min(filter(lambda x: x > 0, delay))
    min_idx = delay.index(min_delay)
    max_idx = delay.index(max(delay))
    print("平均延误时间：%.3f" % np.mean(delay))
    # 绘图
    x = np.linspace(1, len(delay), len(delay))
    rects = plt.bar(x, delay, 0.5, label='other delay plan', color="w", edgecolor="k")
    plt.bar(min_idx + 1, min_delay, 0.5, label='min delay plan', color="w", edgecolor="k", hatch="\\\\\\\\")
    plt.bar(max_idx + 1, max(delay), 0.5, label='max delay plan', color="w", edgecolor="k", hatch="/////")
    plt.plot(x, [np.mean(delay) for _ in range(len(x))], label='mean delay line', color='gray', linestyle='--')
    # 在条形图上加标注（水平居中）
    for i in range(0, len(rects)):
        if i == min_idx or i == max_idx:
            rect = rects[i]
            height = rect.get_height()
            plt.text(rect.get_x() + rect.get_width() / 2, height + 0.3, str(round(height, 3)), ha='center',
                     fontproperties="Times New Roman", size=8.5)
    # 设置坐标轴范围
    plt.xlim([0, len(delay) + 1])
    plt.ylim([min_delay - 10, max(delay) + 10])
    # 设置坐标轴刻度
    # plt.xticks(range(0, 20, 1))
    # plt.yticks(np.arange(50, 72, 2))
    # 设置坐标轴名称
    plt.xlabel("固定配时方案", fontproperties="SimSun", size=10.5)
    plt.ylabel("平均延误时间(s)", fontproperties="SimSun", size=10.5)
    # 设置网格
    # plt.grid()
    # 设置边框
    # plt.axis('off')
    # 设置图例
    legend = ['mean delay line', "other delay plan", "min delay plan", "max delay plan"]
    plt.legend(legend, loc="best", frameon=False)
    # 设置标题
    # plt.title("The Delay Curve", fontproperties="Times New Roman", size=10.5)
    # 保存图片
    plt.savefig(save_img1, dpi=600)
    # 关闭绘图
    plt.close()
    # 绘制固定配时最小延误曲线
    names = ["step", "delay"]
    data = pd.read_csv(result_file[:-4] + '_' + str(min_idx + 1) + '.txt', sep="\s+", names=names)
    delay = list(data["delay"].values)
    mean_delay = np.mean(delay)
    x = np.linspace(0, len(delay) - 1, len(delay))
    plt.plot(x, delay, color='black', marker='D', linestyle='-', linewidth='1.0')
    plt.plot(x, [mean_delay for _ in range(len(x))], color='gray', linestyle='--')
    # 设置坐标轴名称
    plt.xlabel("信号周期", fontproperties="SimSun", size=10.5)
    plt.ylabel("平均延误时间(s)", fontproperties="SimSun", size=10.5)
    plt.savefig(save_img2, dpi=600)
    # 关闭绘图
    plt.close()
    print("绘图完成...")


# 获取vissim环境
def get_vissim_env(plans):
    # 启动vissim
    path = run_vissim()
    if path is not None:
        # 初始化vissim环境
        env = VisCom(path, net_path, simulation, plans)
        return env


# 获取磁盘列表
def get_disklist():
    disk_list = []
    for c in string.ascii_uppercase:
        disk = c + ':'
        if os.path.isdir(disk):
            disk_list.append(disk)
    return disk_list


# 搜索文件路径
def search_file(path):
    for root, dirs, files in os.walk(path):
        for f in files:
            file_path = os.path.abspath(os.path.join(root, f))
            if file_path.endswith("vissim.exe"):
                return file_path
    return None


# 运行vissim软件
def run_vissim():
    # path = r"E:\Program Files (x86)\PTV_Vision\VISSIM430\Exe\vissim.exe"
    path = None
    for disk in get_disklist():
        path = search_file(disk + r"\Program Files (x86)\PTV_Vision")
        if path is not None:
            command = r'%s\RunAsDate.exe 01\01\2008 00:04:10 "%s"' % (project_path, path)
            os.system(command)
            break
        else:
            pass
    else:
        print("未找到vissim软件")
    return path


# 检查文件路径
def check_path():
    path_list = ['./fix', './fix/png', './fix/txt']
    for path in path_list:
        if os.path.exists(path):
            pass
        else:
            os.mkdir(path)


# CPU电源模式配置(开启高性能模式)
def power_config(index=2):
    import subprocess
    mode = [
        "a1841308-3541-4fab-bc81-f71556f20b4a",  # 节能
        "381b4222-f694-41f0-9685-ff5bb260df2e",  # 平衡
        "8c5e7fda-e8bf-4a96-9a85-a6e23a8c635c",  # 高性能
        "e9a42b02-d5df-448d-aa00-03f14749eb61",  # 卓越
    ]
    subprocess.call("Powercfg -s %s" % mode[index])


if __name__ == '__main__':
    power_config()
    check_path()
    # 信号周期、最短绿时、最长绿时、绿时变化间隔、黄灯时间、各相位全红时间
    # timming = [170, 41, 57, 2, 3, [2, 2, 2]]
    # plans, unuesd_plan = create_plans(timming, plans_file)
    # plans = [[170, 3, [2, 2, 2], [40, 69, 46]]]
    # plans = read_plans(plan_file)
    # env = get_vissim_env(plans)
    # test_fix_timming(env, plans)
    draw_result()
